import java.io.*;
import java.util.*;

public class VSensorLoader extends Thread {

	private String pluginsDir;
	private boolean isActive = true;
	private static int VSENSOR_LOADER_THREAD_COUNTER = 0;
	private static VSensorLoader singleton = null;

	private HashMap<Integer, SocketSender> senders;

	public VSensorLoader() {

	}

	public VSensorLoader(String pluginsPath) {
		this.pluginsDir = pluginsPath;
		senders = new HashMap<Integer, SocketSender>();
	}

	public static VSensorLoader getInstance(String path) {
		if (singleton == null)
			singleton = new VSensorLoader(path);
		return singleton;
	}

	public void startLoading() {
		Thread thread = new Thread(this);
		thread.setName("VSensorLoader-Thread" + VSENSOR_LOADER_THREAD_COUNTER++);
		thread.start();
	}

	public void run() {
		while (isActive) {
			Set<Integer> status = new HashSet<Integer>();
			loadPlugin();
			for (String config : Mappings.getAllKnownFileName()) {
				// when system halt,terminate all active sensor threads
				VirtualSensor sensor = Mappings
						.getVSensorInstanceByFileName(config);
				System.out.println(config);
				if (!sensor.isLive())
					status.add(0);
				else
					status.add(1);
			}
			if (status.size() == 1) {
				Iterator<Integer> iter = status.iterator();
				if (iter.next() == 0)
					this.stopLoading();

			}
		}
	}

	public synchronized void loadPlugin() {
		Modifications modifications = getUpdateStatus(pluginsDir);
		ArrayList<VSensorConfig> removeIt = modifications.getRemove();
		ArrayList<VSensorConfig> addSensorIt = modifications.getAdd();
		for (VSensorConfig config : removeIt)
			removeVirtualSensor(config);
		for (VSensorConfig vsconfig : addSensorIt) {
			if (!createVSensorInstance(vsconfig))
				System.out.println(vsconfig.getFileName() + " is not valid!");
		}
		try {
			Thread.sleep(3000);
		} catch (InterruptedException e) {

			e.printStackTrace();
		}

	}

	private synchronized boolean createVSensorInstance(VSensorConfig vs) {
		if (!isVirtualSensorValid(vs))
			return false;

		int gatewayId = vs.getGatewayId();
		SocketSender sender = senders.get(gatewayId);
		if (sender == null) {
			sender = new SocketSender("192.168.16.223", 30003);
			// sender = new SocketSender("127.0.0.1", 30003);
			senders.put(gatewayId, sender);
			sender.start();
		}
		VirtualSensor pool;
		if (vs.getSensorType().equalsIgnoreCase("Temperature")) {
			pool = new TemperatureSensor(vs, sender);
		} else if (vs.getSensorType().equalsIgnoreCase("Humidity")) {
			pool = new HumiditySensor(vs, sender);
		} else {
			pool = new VirtualSensor(vs, sender);
		}
		sender.addVirtualSensor(pool);
		System.out.println(pool.getLastModifiedTime());
		if (Mappings.addVSensorInstance(pool)) {
			pool.startThread();
		}
		return true;
	}

	private void removeVirtualSensor(VSensorConfig config) {
		VirtualSensor sensor = Mappings.getVSensorInstanceByFileName(config
				.getFileName());
		int gatewayId = config.getGatewayId();
		SocketSender sender = senders.get(gatewayId);
		sender.removeVirtualSensor(sensor);
		System.out.println(config.getFileName() + " is removed!");
		if (sender.getSensors().isEmpty()) {
			senders.remove(gatewayId);
			sender.close();
		}
		Mappings.removeConfigByFileName(config.getFileName());
		sensor.close();// terminate sensor thread
	}

	public boolean isVirtualSensorValid(VSensorConfig config) {
		String vsName = config.getName();
		String vsFileName = config.getFileName();
		if (Mappings.getVSensorConfig(vsName) != null)
			return false;
		if (Mappings.getVSensorInstanceByFileName(vsFileName) != null)
			return false;
		if (!isValidJavaIdentifier(vsName))
			return false;
		return true;
	}

	static protected boolean isValidJavaIdentifier(final String name) {
		boolean valid = true;
		final int count = name.length();
		for (int i = 0; i < count; i++) {
			if (false == Character.isJavaIdentifierStart(name.charAt(i))
					&& (false == Character.isDigit(name.charAt(i)))) {
				valid = false;
				break;
			}
		}
		return valid;
	}

	public static Modifications getUpdateStatus(String virtualSensorsPath) {
		return getUpdateStatus(virtualSensorsPath, null);
	}

	public static Modifications getUpdateStatus(String virtualSensorsPath,
			String filterFileName) {
		ArrayList<String> remove = new ArrayList<String>();
		ArrayList<String> add = new ArrayList<String>();

		String[] previous = Mappings.getAllKnownFileName();

		FileFilter filter = new FileFilter() {
			public boolean accept(File file) {
				if (!file.isDirectory() && file.getName().endsWith(".xml")
						&& !file.getName().startsWith("."))
					return true;
				return false;
			}
		};

		File files[] = new File(virtualSensorsPath).listFiles(filter);
		Arrays.sort(files, new Comparator<File>() {
			@Override
			public int compare(File a, File b) {
				return a.getName().compareTo(b.getName());
			}
		});

		main: for (String pre : previous) {
			for (File curr : files)
				if (pre.equals(curr.getAbsolutePath())
						&& (Mappings.getLastModifiedTime(pre) == curr
								.lastModified()))
					continue main;
			remove.add(pre);
		}
		// ---adding the new files to the Add List a new file should added if
		// 1. it's just deployed.
		// 2. it's modification time changed.

		main: for (File cur : files) {
			for (String pre : previous)
				if (cur.getAbsolutePath().equals(pre)
						&& (cur.lastModified() == Mappings
								.getLastModifiedTime(pre)))
					continue main;
			add.add(cur.getAbsolutePath());
		}
		Modifications result = new Modifications(add, remove);
		return result;
	}

	public void stopLoading() {
		this.isActive = false;
		this.interrupt(); // interrupt current thread,VSensorLoader
		for (String configFile : Mappings.getAllKnownFileName()) {
			// when system halt,terminate all active sensor threads
			VirtualSensor sensorInstance = Mappings
					.getVSensorInstanceByFileName(configFile);
			sensorInstance.close();
		}
		System.exit(0);
	}

}
